package com.ysy.service.impl;


import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.github.benmanes.caffeine.cache.Cache;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.ysy.dto.IdRequestDTO;
import com.ysy.dto.IdsRequestDTO;
import com.ysy.dto.UserDTO;
import com.ysy.dto.UserReqDTO;
import com.ysy.entity.User;
import com.ysy.mapper.UserMapper;
import com.ysy.persimmons.common.constant.RedisConstants;
import com.ysy.persimmons.common.enums.StatusEnum;
import com.ysy.persimmons.common.response.Response;
import com.ysy.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author yangSy
 * @since 2025-04-21
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private  UserMapper userMapper;

    @Resource
    private  RedisTemplate<String,Object> redisTemplate;


    /**
     * 用户信息本地缓存,也可以用good的那个还可以去差，这里就先这样
     */
    private static final Cache<Long, UserDTO> LOCAL_USER_CACHE = Caffeine.newBuilder()
            .initialCapacity(10000) // 初始容量为 10000 个条目
            .maximumSize(10000) // 最大容量为 10000 个条目
            .expireAfterWrite(1, TimeUnit.HOURS) // 设置缓存条目在写入后 1 小时过期
            .build();
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
//            .build(new CacheLoader<Long, UserDTO>() {
//                @Override
//                public @Nullable UserDTO load(Long aLong) throws Exception {
//                    redisTemplate.opsForValue().get("user:" + aLong);
//
//                    return null;
//                }
//            });



    @Override
    public Response<List<UserDTO>> findByIds(IdsRequestDTO idsRequestDTO) {

        List<UserDTO> dtoList = new ArrayList<>();

        // 先从缓存中获取
        List<Long> ids = idsRequestDTO.getIds();
        List<Long> idNo = new ArrayList<>();

        // 一级本地 注意这里适合少数据，多的化后续有时间再优化
        ids.forEach(id -> {
            UserDTO ifPresent = LOCAL_USER_CACHE.getIfPresent(id);
            if (ifPresent != null) {
                dtoList.add(ifPresent);
            }else {
                idNo.add(id);
            }
        });
        if (idNo.isEmpty()) {
            // help gc
            ids.clear();
            idNo.clear();
            return Response.success(dtoList);
        }
        // 从redis中取查询
        List<String> redisKeyList =  idNo.stream().map(RedisConstants::buildUserInfoKey).toList();

        List<Object> redisSelectlist = redisTemplate.opsForValue().multiGet(redisKeyList);
        List<UserDTO> userDTOList = new ArrayList<>();
        if (redisSelectlist != null && !redisSelectlist.isEmpty()) {
            userDTOList = redisSelectlist
                    .stream()
                    .filter(Objects::nonNull)
                    .map(e -> JSONObject.parseObject(String.valueOf(e), UserDTO.class))
                    .toList();
        }
        // help gc
        if (redisSelectlist != null) {
            redisSelectlist.clear();
        }
        if (!userDTOList.isEmpty() && userDTOList.size()==idNo.size()) {
            // 添加本地缓存
            userDTOList.forEach(userDTO -> {
                LOCAL_USER_CACHE.put(userDTO.getId(),userDTO);
            });
            dtoList.addAll(userDTOList);
            return Response.success(dtoList);
        }

        // 还有缺少的 求差集
        idNo.removeAll(userDTOList.stream().map(UserDTO::getId).toList());

        // 全部没有去数据库查询
        List<UserDTO> users = userMapper.selectSimpleUsers(idNo);
        dtoList.addAll(users);

        // 异步存储到redis中
        threadPoolTaskExecutor.submit(()->{
            redisTemplate.executePipelined((RedisCallback<Void>) redisConnection -> {
                for (UserDTO user : users) {
                    String key = RedisConstants.buildUserInfoKey(user.getId());
                    // 保底一个小时
                    int expireTime = 60 * 60 + RandomUtil.randomInt(10);
                    redisTemplate.opsForValue().set(key, JSONObject.toJSONString(user), expireTime, TimeUnit.SECONDS);
                }
                return null;
            });
        });

        return Response.success(dtoList);
    }

    @Override
    public Response<UserDTO> findById(IdRequestDTO idRequestDTO) {

        Long id = idRequestDTO.getId();

        // 一级本地
        UserDTO userDTO = LOCAL_USER_CACHE.getIfPresent(id);
        if (Objects.nonNull(userDTO)) {
            return Response.success(userDTO);
        }
        // 二级查询redis
        String redisUserKey = RedisConstants.buildUserInfoKey(id);
        String redisUserString = (String) redisTemplate.opsForValue().get(redisUserKey);
        if (Objects.nonNull(redisUserString)) {
            UserDTO jsonObject = JSONObject.parseObject(redisUserString,UserDTO.class);
            LOCAL_USER_CACHE.put(id, jsonObject);
            return Response.success(jsonObject);
        }

        // 数据库查询，这里可以加锁限制恶意请求
        User user = userMapper.selectById(idRequestDTO.getId());
        if ( user == null ) {
            // 可以异步同步加速
            LOCAL_USER_CACHE.put(id, new UserDTO());
            // 后续在补充错误码
            return Response.fail("用户不存在");
        }
        UserDTO dto = new UserDTO();
        dto.setId(user.getId());
        dto.setName(user.getName());
        dto.setPhone(user.getPhone());
        dto.setStatus(user.getStatus());
        dto.setAvatar(user.getAvatar());
        dto.setIntroduction(user.getIntroduction());
        // 可以异步同步加速,优化
        String jsonString = JSONObject.toJSONString(dto);
        // 5-10分钟
        long expireSeconds = 5 + RandomUtil.randomInt(5);
        redisTemplate.opsForValue().set(redisUserKey, jsonString, expireSeconds, TimeUnit.MINUTES);

        return Response.success(dto);
    }

    @Override
    public Response<Long> registerUserByPhone(UserReqDTO userReqDTO) {

        String phone = userReqDTO.getPhone();

        // 先判断该手机号是否已被注册
        User user = lambdaQuery().eq(User::getPhone, phone).one();

        // 若已注册，则直接返回用户 ID
        if (Objects.nonNull(user)) {
            return Response.success(user.getId());
        }

        // 否则注册新用户
        String account = UUID.randomUUID().toString().substring(2, 6);;
        User userDO = User.builder()
                .phone(phone)
                .account(account)
                .name("欢迎您" + account) //
                .status(StatusEnum.ENABLE.getValue()) // 状态为启用
                .createTime(LocalDateTime.now())
                .updateTime(LocalDateTime.now())
                .build();
        userMapper.insert(userDO);
        Long userId = userDO.getId();
        return Response.success(userId);
    }

    @Override
    public Response<?> updatePassword(UserReqDTO userReqDTO) {
        // 判断新旧密码

        return null;
    }

    @Override
    public Response<UserDTO> findByPhone(UserReqDTO userReqDTO) {
        User user = lambdaQuery().eq(User::getPhone, userReqDTO.getPhone()).one();
        if (user == null) {
            return Response.fail();
        }
        UserDTO dto = new UserDTO();
        dto.setId(user.getId());
        dto.setName(user.getName());
        dto.setPhone(user.getPhone());
        dto.setAvatar(user.getAvatar());
        return Response.success(dto);
    }
}
